#pragma once

#include "e2d/core/Object.h"
#include "e2d/core/Scene.h"
#include "e2d/core/Type.h"

namespace e2d
{

	struct Entity :public Object, public fxcc::graph::common::AnimNode
	{
		Entity(entt::entity entity, e2d::Scene* scene, const std::string& name) :Object(), m_Scene(scene), m_Entity(entity),
			m_Name(name)
		{

		};

		template<typename T>
		T& Emplace(const T& t)
		{
			auto& _t = m_Scene->rg.emplace<T>(m_Entity, t);
			_t.m_Entity = this;
			return _t;
		}

		template<typename T>
		T& TryEmplace(const T& t, bool load = true)
		{
			if (HasComponent<T>())
			{
				return Get<T>();
			}
			else
			{
				auto& res = Emplace<T>(t);
				
				if (load)
					res.OnLoad();
				return res;
			}
		}

		template<typename T>
		T& EmplaceOrReplace(const T& t)
		{
			auto& _t = m_Scene->rg.emplace_or_replace<T>(m_Entity, t);
			_t.m_Entity = this;
			return _t;
		}

		template<typename T>
		T& Get()
		{
			return m_Scene->rg.get<T>(m_Entity);
		};


		template<typename T>
		const T GetConst() const
		{
			return m_Scene->rg.get<T>(m_Entity);
		};

		template<typename T>
		T& GetOrEmplace()
		{
			auto& _t = m_Scene->rg.get_or_emplace<T>(m_Entity);
			_t.m_Entity = this;
			return _t;
		};

		template<typename T>
		bool HasComponent() const
		{
			return m_Scene->rg.any_of<T>(m_Entity);
		}
		template<typename T>
		bool HasComponetAndExist() const
		{
			if (HasComponent<T>())
			{

				const auto& t = GetConst<T>();
				return t.m_Exist;
			}
			return false;
		}

		template<typename T>
		bool CopyComponent(const e2d::Entity* other, bool load = true)
		{
			if(!other)
			{
				return false;
			}
			if(!other->HasComponent<T>())
			{
				return false;
			}
			auto& t = this->EmplaceOrReplace(other->GetConst<T>());
			
			if (load)
				t.OnLoad();
			return true;
		}
		template<typename T>
		bool AddComponentFromType(const ComponentType& type)
		{
			bool flag = (type == T::_Label_());
			if (flag)
			{
				auto& t = GetOrEmplace<T>();
				t.OnLoad();

			}

			return flag;
		}

	public:

		entt::entity m_Entity{ entt::null };

		e2d::Scene* m_Scene{ 0 };

		e2d::Entity* m_Parent{ 0 };

		std::vector<std::shared_ptr<e2d::Entity>> m_Children;

		std::unordered_map<std::string, std::shared_ptr<e2d::Entity>> m_ChildrenMap;

		std::string m_Name;

		std::vector<e2d::Entity*> GetChildrenExistCalculate() const;

		std::vector<e2d::Entity*> GetChildren(bool needExist = true) const;

		template<typename T>
		std::vector<e2d::Entity*> GetComponentChildren(bool needExist = true) const
		{
			std::vector<e2d::Entity*> res;

			for (auto it : m_Children)
			{
				bool exist = it->ExistCalculate();
				if ((needExist && exist) || !needExist)
				{
					if (it->HasComponent<T>())
					{
						res.push_back(it.get());
					}
				}
			}

			return res;
		}

		template<typename T>
		std::vector<e2d::EntityMap> GetComponentChildrenMap(bool needExist = true) const
		{
			auto entities = GetComponentChildren<T>(needExist);
			std::vector<e2d::EntityMap> res;

			for (auto e : entities) {
				res.push_back(e);
			}
			return res;

		}
		bool HasParent()const;

		bool HasChild() const;

		bool HasChild(const std::string& uuid)const;

		void RegisterChild(std::shared_ptr<e2d::Entity> entity);

		bool SnatchEntityAsChild(const std::string& uuid);

		bool SnatchEntityAsChild(std::shared_ptr<e2d::Entity> entity);

		void DisappearFromParentChildren();

		void AddChild(std::shared_ptr<e2d::Entity> child);

		bool RemoveChild(const std::string& uuid);
		
		bool RemoveChildren(const std::vector<std::string>& uuid);

		std::shared_ptr<e2d::Entity> NewChild();

		void UploadChildrenMap();
		
		bool ExistCalculate() const ;

		bool IsGrandChildOf(e2d::Entity* other) const;


		template<typename T>
		bool RemoveComponent()
		{
			if (HasComponent())
			{
				m_Scene->rg.remove<T>(m_Entity);
				return true;
			}
			return false;
		}

		void SetTranform(const fxcc::graph::common::Transform& transform) override;

		fxcc::graph::common::Transform GetLocalTransform()  override;

		fxcc::graph::common::Transform GetWorldTransform()  override;

	public:

		std::string m_NewComponentType;
	};
};